package cn.jtool.dig.client.comm.tunnel;

import cn.jtool.dig.client.comm.VirtualManager;
import cn.jtool.dig.client.configuration.TunnelProperties;
import cn.jtool.dig.common.enums.TunnelResultCode;
import cn.jtool.dig.common.model.Commands;
import cn.jtool.dig.common.model.Packet;
import cn.jtool.dig.common.model.PacketBody;
import cn.jtool.dig.common.model.PacketHeader;
import cn.jtool.dig.common.netty.CtxUtils;
import cn.jtool.dig.common.netty.SNGenerator;
import cn.jtool.dig.common.netty.TcpClient;
import cn.jtool.dig.common.util.SpringContextUtil;
import cn.jtool.knife.core.enums.BaseCode;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@ChannelHandler.Sharable
public class TunnelConnHander extends ChannelInboundHandlerAdapter {

    @Resource
    private TunnelProperties tunnelProperties;

    @Resource
    private ConfigurableApplicationContext context;


    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("[隧道]连接成功, 远程地址: {}", ctx.channel().remoteAddress());
        this.sendAuth(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("[隧道]连接断开, 远程地址: {}", ctx.channel().remoteAddress());
        VirtualManager.close();
        Integer authState = CtxUtils.getAttribute(ctx, TunnelAttrKey.AUTH_STATUS);
        if(Objects.isNull(authState)) {
            ctx.executor().schedule(() -> {
                log.info("[隧道]重新连接, 尝试中...");
                TcpClient tunnelClient = SpringContextUtil.getBean("tunnelClient", TcpClient.class);
                tunnelClient.reConnect(Integer.MAX_VALUE);
            }, 100, TimeUnit.MILLISECONDS);
        }else{
            TunnelResultCode tunnelResultCode = BaseCode.fromCode(TunnelResultCode.class, authState);
            if(Objects.isNull(tunnelResultCode)){
                log.error("[隧道]授权异常, 状态码: {}", authState);
                return;
            }
            if(TunnelResultCode.LICENSE_ON_LINE == tunnelResultCode
                ||TunnelResultCode.LICENSE_DISABLE == tunnelResultCode
                ||TunnelResultCode.LICENSE_EXPIRED == tunnelResultCode
                ||TunnelResultCode.LICENSE_NOT_EFFECTIVE == tunnelResultCode){
                log.error("[隧道]授权异常, 状态码: {}, 状态描述: {}", tunnelResultCode.getCode(), tunnelResultCode.getDesc());
                ctx.executor().schedule(() -> {
                    log.info("[隧道]重新连接, 尝试中...");
                    TcpClient tunnelClient = SpringContextUtil.getBean("tunnelClient", TcpClient.class);
                    tunnelClient.reConnect(Integer.MAX_VALUE);
                }, 10, TimeUnit.SECONDS);
            }else{
                log.error("[隧道]授权异常, 状态码: {}, 状态描述: {}", tunnelResultCode.getCode(), tunnelResultCode.getDesc());
                ctx.channel().close();
                SpringApplication.exit(context, () -> 0);
                log.error("退出应用");
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("[隧道]连接异常, 远程地址: {}", ctx.channel().remoteAddress(), cause);
        this.channelInactive(ctx);
    }

    private void sendAuth(ChannelHandlerContext ctx) {
        PacketHeader header = new PacketHeader();
        header.setCommand(Commands.AUTH.getCode());
        header.setDigId(0x00);
        header.setChannelId(0x00L);
        header.setSn(SNGenerator.gen());

        PacketBody body = new PacketBody();
        byte[] secretKeyBytes = new byte[0];
        try {
            secretKeyBytes = this.tunnelProperties.getSecretKey().getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
            log.error("密钥信息异常, {}", this.tunnelProperties.getSecretKey());
            this.channelInactive(ctx);
        }
        body.writeByte(secretKeyBytes.length);
        body.write(secretKeyBytes);
        Packet packet = Packet.builder().header(header).body(body).build();
        ctx.writeAndFlush(packet);
    }
}
